<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>webgl-example</title>
    <link rel="stylesheet" href="../css/common.css">
</head>

<body>
    <canvas id="webgl" width="512" height="512"></canvas>
</body>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script src="../lib/GLSLLoader.js"></script>

<script>

    var GLSLLoader = new GLSLLoader();

    var shaders = [
        './GLSL/09.vertex_shader.glsl',
        './GLSL/09.fragment_shader.glsl'
    ];

    GLSLLoader.loadVertexFragment(shaders, main);

    // 离屏绘制的尺寸
    var OFFSCREEN_WIDTH = 512;
    var OFFSCREEN_HEIGHT = 512;

    function main(shader) {

        var vertex_shader = shader.vertex;
        var fragment_shader = shader.fragment;

        // 获取canvas dom对象
        var canvas = document.getElementById('webgl');

        // get webglRenderingContext for webgl
        var gl = getWebGLContext(canvas);
        if (!gl) {
            console.log('failed to get the rendering context for WebGL');
            return false;
        }

        if (!initShaders(gl, vertex_shader, fragment_shader)) {
            console.log('初始化着色器失败');
            return false;
        }

        var program = gl.program;
        // 获取变量存储位置
        program.a_Position = gl.getAttribLocation(program, 'a_Position');
        program.a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');
        program.u_MvpMatrix = gl.getUniformLocation(program, 'u_MvpMatrix');

        if (program.a_Position < 0 ||
            program.a_TexCoord < 0 ||
            !program.u_MvpMatrix) {
            console.log('获取变量存储位置失败');
            return false;
        }

        // 初始化立方体和平面顶点信息
        var cube = initVertexBuffersForCube(gl);
        var plane = initVertexBuffersForPlane(gl);

        if (!cube || !plane) {
            console.log('初始化顶点信息失败');
            return false;
        }

        var texture = initTextures(gl);
        if (!texture) {
            console.log('初始化纹理信息失败');
            return false;
        }

        // 初始化帧缓冲区
        var fbo = initFramebufferObject(gl);
        if (!fbo) {
            console.log('初始化帧缓存对象失败');
            return false;
        }

        gl.enable(gl.DEPTH_TEST);

        // 隐藏消除功能
        gl.enable(gl.CULL_FACE);

        var viewProjMatrix = new Matrix4();
        viewProjMatrix.setPerspective(30, canvas.width / canvas.height, 1.0, 100.0);
        viewProjMatrix.lookAt(0.0, 0.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

        var viewProMatrixFBO = new Matrix4();
        viewProMatrixFBO.setPerspective(30.0, OFFSCREEN_WIDTH / OFFSCREEN_HEIGHT, 1.0, 100.0);
        viewProMatrixFBO.lookAt(0.0, 2.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

        var currentAngle = 0.0;
        var tick = function () {
            currentAngle = animate(currentAngle);
            draw(gl, canvas, fbo, plane, cube, currentAngle, texture, viewProjMatrix, viewProMatrixFBO);
            window.requestAnimationFrame(tick, canvas);
        }
        tick();
    }

    var ANGLE_STEP = 30;   // The increments of rotation angle (degrees)

    var last = Date.now(); // Last time that this function was called
    function animate(angle) {
        var now = Date.now();   // Calculate the elapsed time
        var elapsed = now - last;
        last = now;
        // Update the current rotation angle (adjusted by the elapsed time)
        var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
        return newAngle % 360;
    }

    function draw(gl, canvas, fbo, plane, cube, angle, texture, viewProMatrix, viewProMatrixFBO) {

        gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);

        gl.clearColor(0.1, 0.6, 0.4, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        drawTexturedCube(gl, gl.program, cube, angle, texture, viewProMatrixFBO);

        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.viewport(0, 0, canvas.width, canvas.height);

        gl.clearColor(0.0, 0.5, 0.5, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        drawTexturedPlane(gl, gl.program, plane, angle, fbo.texture, viewProMatrix);

    }

    var g_modelMatrix = new Matrix4();
    var g_mvpMatrix = new Matrix4();

    function drawTexturedCube(gl, program, o, angle, texture, viewProMatrix) {

        // 计算模型矩阵
        g_modelMatrix.setRotate(10.0, 1.0, 0.0, 0.0);
        g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0);

        g_mvpMatrix.set(viewProMatrix);
        g_mvpMatrix.multiply(g_modelMatrix);
        gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

        drawTexturedObject(gl, program, o, texture);

    }

    function drawTexturedPlane(gl, program, o, angle, texture, viewProMatrix) {

        g_modelMatrix.setTranslate(0, 0, 1);
        g_modelMatrix.rotate(20.0, 1.0, 0.0, 0.0);
        g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0);

        g_mvpMatrix.set(viewProMatrix);
        g_mvpMatrix.multiply(g_modelMatrix);
        gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

        drawTexturedObject(gl, program, o, texture);

    }

    function drawTexturedObject(gl, program, o, texture) {
        
        initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
        initAttributeVariable(gl, program.a_TexCoord, o.texCoordBuffer);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);
        gl.drawElements(gl.TRIANGLES, o.numIndices, o.indexBuffer.type, 0);

    }

    // 立方体顶点信息数据
    function initVertexBuffersForCube(gl) {

        // Vertex coordinates
        var vertices = new Float32Array([
            1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0,    // v0-v1-v2-v3 front
            1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0,    // v0-v3-v4-v5 right
            1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0,    // v0-v5-v6-v1 up
            -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0,    // v1-v6-v7-v2 left
            -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,    // v7-v4-v3-v2 down
            1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0     // v4-v7-v6-v5 back
        ]);

        // Texture coordinates
        var texCoords = new Float32Array([
            1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,    // v0-v1-v2-v3 front
            0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0,    // v0-v3-v4-v5 right
            1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0,    // v0-v5-v6-v1 up
            1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,    // v1-v6-v7-v2 left
            0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // v7-v4-v3-v2 down
            0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0     // v4-v7-v6-v5 back
        ]);

        // Indices of the vertices
        var indices = new Uint8Array([
            0, 1, 2, 0, 2, 3,    // front
            4, 5, 6, 4, 6, 7,    // right
            8, 9, 10, 8, 10, 11,    // up
            12, 13, 14, 12, 14, 15,    // left
            16, 17, 18, 16, 18, 19,    // down
            20, 21, 22, 20, 22, 23     // back
        ]);

        var obj = new Object();

        obj.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
        obj.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT);
        obj.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);

        if (!obj.vertexBuffer ||
            !obj.texCoordBuffer ||
            !obj.indexBuffer) {
            return null;
        }

        obj.numIndices = indices.length;

        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

        return obj;

    }

    // 平面顶点信息数据
    function initVertexBuffersForPlane(gl) {

        var vertices = new Float32Array([
            1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0    // v0-v1-v2-v3
        ]);

        // Texture coordinates
        var texCoords = new Float32Array([1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0]);

        // Indices of the vertices
        var indices = new Uint8Array([0, 1, 2, 0, 2, 3]);

        var obj = new Object();

        obj.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
        obj.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT);
        obj.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);

        obj.numIndices = indices.length;

        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

        return obj;

    }

    // 初始化纹理
    function initTextures(gl) {

        // 创建纹理对象
        var texture = gl.createTexture();
        if (!texture) {
            console.log('failed to create the texture object');
            return null;
        }

        var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
        if (!u_Sampler) {
            console.log('获取取样器存储地址失败');
            return null;
        }

        // 创建一个图片对象
        var image = new Image();

        if (!image) {
            console.log('failed to create the image object');
            return null;
        }

        image.onload = function () {

            // 写入照片数据到image对象
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
            // gl.activeTexture(gl.TEXTURE0);//激活 0号纹理单元
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

            gl.uniform1i(u_Sampler, 0);

            // 解绑纹理对象
            gl.bindTexture(gl.TEXTURE_2D, null);
        }

        image.src = '../resources/ubuntu.jpg';

        return texture;

    }


    function initFramebufferObject(gl) {

        var framebuffer, texture, depthBuffer;

        // 定义出错异常处理
        var error = function () {

            if (framebuffer) gl.deleteFramebuffer(framebuffer);
            if (texture) gl.deleteTexture(texture);
            if (depthBuffer) gl.deleteRenderbuffer(depthBuffer);

            return null;
        }

        // 创建帧缓冲区
        framebuffer = gl.createFramebuffer();
        if (!framebuffer) {
            console.log('创建帧缓存失败');
            return error();
        }

        // 创建纹理对象并设置其尺寸和参数
        texture = gl.createTexture();
        if (!texture) {
            console.log('创建纹理失败');
            return error();
        }

        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        framebuffer.texture = texture;

        depthBuffer = gl.createRenderbuffer();
        if (!depthBuffer) {
            console.log('初始化深度对象失败');
            return error();
        }

        // 将纹理和渲染缓冲区对象关联到帧缓冲区对象上
        gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);

        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);

        // 检查帧缓冲区是否被正确设置
        var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
        if (gl.FRAMEBUFFER_COMPLETE !== e) {
            console.log('帧缓存对象是不完整的：' + e.toString());
            return error();
        }

        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);

        return framebuffer;

    }

    function initAttributeVariable(gl, a_attribute, buffer) {
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
        gl.enableVertexAttribArray(a_attribute);
    }

    // 创建缓冲区对象
    function initArrayBufferForLaterUse(gl, data, num, type) {
        // 创建缓冲区对象
        var buffer = gl.createBuffer();

        if (!buffer) {

            console.log('failed to create the buffer object');
            return null;

        }

        // write data into the buffer object
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

        buffer.num = num;
        buffer.type = type;

        return buffer;

    }

    // 创建带顶点索引的缓冲区对象
    function initElementArrayBufferForLaterUse(gl, data, type) {

        var buffer = gl.createBuffer();
        if (!buffer) {
            console.log('failed to create the buffer object');
            return null;
        }

        // write data into buffer object
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);

        buffer.type = type;

        return buffer;

    }

</script>

</html>